Programming oriented around objects
"object" : A structure with both data and functions
Similar to a C or IDL struct
, but with functions "bound" to them.
fobj = open('../README.md')
fobj.name
'../README.md'
fobj.readline()
'PyLunch Nova <sup>*</sup>\n'
print(fobj.tell())
fobj.readline()
print(fobj.tell())
26 52
Everything in Python is an object. (Even the classes themselves! Mind-bending...)
Objects hide details you don't care about (E.g., where am I in the file), and provide a unified view of what you do.
Objects make it easier for you to extend someone else's work without (E.g., domain-specific coordinates in astropy
, your own point spread function in photutils
).
MyClassName
.__init__
: A special name Python uses for the method called when a new object is instantiatedGo to https://github.com/spacetelescope/pylunch and clone it (if you know git). Then just find the Object Oriented Intro.ipynb
file.
Go to the same page and click on: session2
-> Object Oriented Intro.ipynb
. Then right click on the "Raw" button (upper right), and "save link as"/"download link" (or whatever is similar for your browser). Or just use this link (its the same thing).
Once you've got it locally, open a terminal, cd
into that directory, and run the command jupyter notebook
.
A point in 2D space has two coordinates. A circle can be thought of as a point with a radius. This is a common use of inheritance: we'll need to define a Point
class to store the x
and y
positions, and a Circle
class (which is a subclass of Point
), which also has a radius
.
Along the way we'll also implement methods to draw the objects (using the matplotlib
plotting package). Don't worry about the details of anything that starts with plt
, just know that it's calling matploltlib
to actually make the plots.
# This cell has some stuff needed to make the "draw" methods work. It's not
# important to understand them for now, but you do need to run them to get
# things to work in the notebook
# This makes notebooks render plots inside the notebook
%matplotlib inline
# And these are imports from the "matplotlib" plotting package (more on that in a future session)
from matplotlib import pyplot as plt
class Point:
def __init__(self, x, y):
# this is the method that gets called when you create a new Point
self.x = x
self.y = y
def draw(self):
plt.scatter([self.x], [self.y])
# if you're on python 2.x, the top line should be:
#class Point(object):
p = Point(1, 2)
p.draw() # You can just call draw, and not worry at all about *how* the drawing happens
class Circle(Point): # This means "define a class Circle that is a subclass of Point"
def __init__(self, x, y, radius):
super().__init__(x,y)
# can also be this (works in py 2.x):
#Point.__init__(self, x, y)
self.radius = radius
def draw(self): # this method "overrides" the `draw` method of Point
super(Circle, self).draw()
# can also be this (works in py 2.x):
#Point.draw(self)
# note for people not familiar with matplotlib: alpha keyword below sets transparency
circle_patch = plt.Circle((self.x, self.y), self.radius, alpha=.5)
plt.gca().add_patch(circle_patch)
def compute_area(self):
from math import pi
return pi * self.radius**2
pointishes = [Point(0.5, 2), Circle(2, 3, 1.2), Point(3.5, 4)]
for obj in pointishes:
obj.draw() # again, all I need to know is that they "can be drawn"
print(pointishes[1].compute_area())
This is a more complex class heirarchy, which includes demonstrating multiple inheritance by way of the "diamond" pattern illustrated below. Also a fairly cynical view of how our science gets done.
<img src="AstronomerClassHeirarchy.svg" width=40%>
class Astronomer:
def __init__(self):
self._science_done = []
def do_science(self):
raise NotImplementedError("What, you think astronomers are all alike? Pick something more specific.")
@property
def cv(self):
return '\n'.join(self._science_done)
class Observer(Astronomer):
def __init__(self, favorite_targets):
self.favorite_targets= favorite_targets
super().__init__()
def do_science(self):
target, success = self.observe()
if success:
self._science_done.append('Sucessfully observed ' + target)
else:
self._science_done.append('Failed to observe ' + target)
success_rate = .1 # TAC+weather if you're ground based, maybe?
def observe(self):
import random
target = self.favorite_targets[random.randint(0, len(self.favorite_targets)-1)]
success = random.random() < self.success_rate
return target, success
class Theorist(Astronomer):
def do_science(self):
theory = self.make_theory()
self._science_done.append('Foundational work on theory of ' + theory)
def make_theory(self):
import random
# Eh, we just make it all up anyway
theory = chr(random.randint(97, 97+25)).upper()
for _ in range(random.randint(4, 10)):
theory += chr(random.randint(97, 97+25))
return theory
class Hybrid(Observer, Theorist):
def __init__(self, fraction_theorist, favorite_targets):
super().__init__(favorite_targets)
self.last_theory = None
self.fraction_theorist = fraction_theorist
def do_science(self):
import random
if self.last_theory is None or (random.random() < self.fraction_theorist):
self.last_theory = self.make_theory()
else:
target, success = self.observe()
if success:
self._science_done.append('Used {} to prove theory {}'.format(target, self.last_theory))
else:
self._science_done.append('Used {} to falsify theory {}'.format(target, self.last_theory))
iva_momcheva = Observer(['AEGIS', 'COSMOS', 'GOODS', 'UDS', 'Clg J0218.3-0510', 'assorted fancy lens clusters'])
erik_tollerud = Hybrid(.4, ['an M31 satellite', 'a Milky Way satellite', 'an isolated LG Dwarf', 'a Local Volume Dwarf'])
for _ in range(20):
iva_momcheva.do_science()
erik_tollerud.do_science()
print("Erik's CV:")
print(erik_tollerud.cv)
print("\nIva's CV:")
print(iva_momcheva.cv)
fritz_zwicky = Theorist()
for _ in range(150):
fritz_zwicky.do_science()
print("Zwicky's CV:")
print(fritz_zwicky.cv)
astropy.coordinates
: http://docs.astropy.org/en/stable/coordinates/index.html#class-inheritance-diagram, and the documentation for how to make your own custom frames: http://docs.astropy.org/en/stable/coordinates/frames.html#defining-a-new-framesuper()
works (you'll probably have some surprises!).